/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package controller;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import model.test.*;
import view.MainGUI;
import view.StudentGUI;
import view.TimerLabel;
import view.Utilities;

/**
 *
 * @author RAMIN & NOORAMI
 */


public class StudentController {

    final StudentGUI gui;
    int ActualSection=0;
    Test test;
    String filepath;
    Boolean isStudent;
    TimerLabel timerLabel;
    
    final URL urlStop = getClass().getResource("/images/stop64.png"); //for icon
    final String imagePath = System.getProperty("user.dir")+"/src/userImages/";

    //constructor 1 WITH TEST NAME
    public StudentController(final StudentGUI gui, String testName, final Boolean isStudent) {

        this.gui = gui;
        this.isStudent = isStudent;

        gui.setTestTaker(isStudent);
        gui.setVisible(true);
        chooseTest(testName,null);  //the Test can be indicated with a testName (therefore test=null) or with a TestObject (therefore testName="")
        setUpListeners();     
    }
    
    //constructor 2 WITH TEST OBJECT
    public StudentController(final StudentGUI gui, Test testObject, final Boolean isStudent) {

        this.gui = gui;
        this.isStudent = isStudent;
                
        gui.setTestTaker(isStudent);
        gui.setVisible(true);
        chooseTest("",testObject);  //the Test can be indicated with a testName (therefore test=null) or with a TestObject (therefore testName="")
        setUpListeners();     
    }
    
    public StudentController(final StudentGUI gui, Test testObject, final Boolean isStudent,
            int sectionNumber) {

        this.gui = gui;
        this.isStudent = isStudent;
        this.ActualSection=sectionNumber;
        
        gui.setTestTaker(isStudent);
        gui.setVisible(true);
        chooseTest("",testObject);  //the Test can be indicated with a testName (therefore test=null) or with a TestObject (therefore testName="")
        setUpListeners();     
        
    }
    
    //setUp listeners for the components in the StydentGUI
    void setUpListeners(){
        
        gui.getBtnNextSection().addActionListener(new ActionListener() {
            @Override
            //Next Section button
            public void actionPerformed(ActionEvent ae) {
                
                //stop timer if it is a student
                if (isStudent) {
                    timerLabel.stopTimer();
                }
                
                if (test.getSections().size() == (ActualSection + 1)) {
                    
                    if (gui.getBtnNextSection().getText() == "Finish Test") {
                        //if it is the last section, the test ends here
                        initialiseResults(test);
                        gui.dispose();
                        endTest();
                        return;
                    }
                }
                
                ActualSection++;     //increment the currect section
                initialiseTest(test);//display the new section
            }
        });  
        
        //previous section button (only visible to setter)
        gui.getBtnPrevious().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                
                //show end of test here
                if (ActualSection == 0){
                    messageDialogWarning("You are currently on the first section !", gui);
                    return;
                }
                if (test.getSections().size() != (ActualSection)){                       
                    changeBtnFinishToNext();
                }
                ActualSection--;        //decrement the currect section
                initialiseTest(test);   //display the new section
            }
        });   

        //retunr to editor button (only visible for setter user)
        gui.getReturnToEditor().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                gui.dispose();  //dispose this actual GUI class,
            }
        }); 
        
    }
    
    void changeBtnNextToFinish(){
        //change button appearence if it is the last section
        gui.getBtnNextSection().setText("Finish Test");
        gui.getBtnNextSection().setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/finishFlag48.png")));
    }
    
    void changeBtnFinishToNext(){
        gui.getBtnNextSection().setText("Next Section");
        gui.getBtnNextSection().setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/nextSec48.png")));
    }
    
    void chooseTest(String testName, Test testObject){
        
        //asign to test instance variable the testObject or open one using the test name
        if (testObject==null){
            this.test=OpsFile.readTestFile(System.getProperty("user.dir")+"/src/savedTestFiles/"+testName+".txt");
        }
        else { 
            this.test=testObject;
        }
        initialiseTest(test);
    }

    void initialiseResults(Test test){
         
        //calculate the Results of the student
        
        //inside this method each the answer of the student is compared to the correct answer
        //also an HTML is build by steps in order to create a table to show results to the user
        
        String resultMessage="";
        
        float marksTot=0;
        int possMarksTot=0;
        float percentageTot=0.0f;

        for (Section currentSection:test.getSections()){//loop for every existing section

            float marksSec=0;
            float possMarksSec=0;
            float percentageSec=0.0f;
            
            for (SubSection subsection:currentSection.getSubSections()){//loop for every subsection in the actual section
                
                for (int j=0; j<subsection.getQuestions().size(); j++){ //loop for every question in the actual subsection
                    
                        Question tmpQuestion = subsection.getQuestions().get(j);    //get the question
                        //MCQ
                        if (tmpQuestion instanceof MCQ){
                            
                            MCQ tempMCQ= (MCQ)tmpQuestion;
                            //award marks if the student answer the correct option
                            if (tmpQuestion.getStudentAnswer().equals(String.valueOf(tempMCQ.getCorrAns()))){
                               marksSec+=tmpQuestion.getMarks();    //update student marks if he got it right
                            }
                            //give penalty if student selects an option and gets it wrong
                            else if(!tmpQuestion.getStudentAnswer().equals("")&& !tmpQuestion.getStudentAnswer().equals(String.valueOf(tempMCQ.getCorrAns()))){
                               marksSec-=tmpQuestion.getPenalty();  //if he got it wrong substract penalty marks
                            }
                            possMarksSec+=tmpQuestion.getMarks(); //update Max possible marks
                        }
                        //FIBQ
                        if (tmpQuestion instanceof FIBQ){
                            
                            FIBQ tempFBQ=(FIBQ)tmpQuestion;
                            //check if the student answer is matches to any of the possible correct answers
                            boolean corr=false; //temporal boolean, that sets to true if student gets it right, then check it to see if penalty is needed
                            for (String possAns:tempFBQ.getCorrAns()){
                                   if (tmpQuestion.getStudentAnswer().equals(possAns)){
                                        corr=true;
                                   }
                            }
                            //award marks or penalty (if no answer do not give penalty)
                            if(corr){
                                marksSec+=tmpQuestion.getMarks(); //update student marks if he got it right
                            }
                            if(!corr && !tmpQuestion.getStudentAnswer().equals("")){//susbtract penalty if he got it wrong (his answer is not equal to any of the posibilities) (do not penalise if the answer is empty)
                                marksSec-=tmpQuestion.getPenalty();
                            }
                            possMarksSec+=tmpQuestion.getMarks(); //update Max possible marks
                        }
                        //MFIBQ
                        if (tmpQuestion instanceof MFIBQ){
                            MFIBQ tempMFBQ=(MFIBQ)tmpQuestion;
                            marksSec+=manualMarking(tempMFBQ); //get marks from manual marking
                            possMarksSec+=tmpQuestion.getMarks();   //update Max posible marks
                        }  
                    } 
                }
                
                marksTot += marksSec;         //update total marks
                possMarksTot += possMarksSec; //update total marks
                percentageSec = ((float) marksSec / (float) possMarksSec) * 100; //calculate percentage
                resultMessage=resultMessage+"<tr><td align=center>"+currentSection.getName()+"</td><td align=center>"+possMarksSec+"</td><td align=center>" + marksSec+"</td><td align=center>" +String.format("%.1f",percentageSec) +" %</td></tr>" ;
            }
        percentageTot = ((float) marksTot / (float) possMarksTot) * 100;
        resultMessage = resultMessage + "<tr BGCOLOR=silver><th>TOTAL</th><td align=center>" + possMarksTot + "</td><td align=center>" + marksTot + "</td><td align=center>" + String.format("%.1f", percentageTot) + "%</td></tr>";
        messageDialogResults("<html> <center><font size=10><br>TEST RESULTS</font><br> <br> <table border=1 cellpadding=5 cellspacing=1><tr BGCOLOR=#A4A4A4><th>SECTION</th><th>MAX POSSIBLE MARKS</th><th>YOUR MARKS</th><th>YOUR PERCENTAGE</th></tr>" + resultMessage + "</table></center></html>", gui);
    }
    
    //creates the message to be shown to the user to input the marking for the especific question, and returns the marking  (until the input is a valid mark)
    private float manualMarking(MFIBQ question) {

        String questionTxt=Utilities.replace(question.getQuestionTxt(), "<blank>", " _________ ");
        String studentAnswer=question.getStudentAnswer();       
        int questionMarks=question.getMarks();
        float questionPenalty=question.getPenalty();
        
        String message = "<html><h3><b>The following question requires manual marking:</b></h1></html>\n\n";
        message += questionTxt + "\n\n";
        message += "<html><b>The student's answer was: </b></html>\n\n";
        
        if (!studentAnswer.equals("")) {
            if (studentAnswer.charAt(studentAnswer.length() - 1) == ',') {
                studentAnswer = studentAnswer.substring(0, studentAnswer.length() - 1); //if the last character is a comma, remove it
            }
            String[] answers = studentAnswer.split(",", -1);
            int i = 1;
            for (String answer : answers) {
                message += i + ". " + answer + "\n";
                i++;
            }
        }
        else{
            message+="Student did not answer anything ! \n";
        }
        
        message +="\n";
        message += "<html><b>Maximum Marks: </b></html>\n";
        message += Integer.toString(questionMarks) + "\n";
        message += "<html><b>Maximum Penalty: </b></html>\n";
        message += Float.toString(questionPenalty) + "\n\n";
        message += "<html><b>Marks awarded: </b></html>\n";
        
        //invoke method until user inputs a mark between penalty and mark
        float marksGiven=manualMarkingDialog(message);
        while(marksGiven>questionMarks || marksGiven<-questionPenalty){
            Utilities.messageDialogWarning("The awarded marks cannot be greater than the MAX MARKS or less than the MAX PENALTY", gui, urlStop);
            marksGiven=manualMarkingDialog(message);
        }
        return marksGiven;
    }
    
    //shows the message, and returns the input of the user, only if its a valid number
    private float manualMarkingDialog(String message){
        
        String userString; //input by user
        float userFloat; //input by user converted to float
        Object[] buttons = { "Continue" };
        
        //ask the user until he enters a valid float string
        userString=(String) JOptionPane.showInputDialog(gui, message, "Manual Marking", JOptionPane.INFORMATION_MESSAGE, null,null , "input marks here");
        while(!Utilities.isFloat(userString) || userString==null){
            Utilities.messageDialogWarning("Please enter a valid number", gui, urlStop);
            userString=(String) JOptionPane.showInputDialog(gui, message, "Manual Marking", JOptionPane.INFORMATION_MESSAGE, null, null, "input marks here");
        }
        
        userFloat=Float.parseFloat(userString);
        return userFloat;
        
    }
    
    void initialiseTest(Test test){
        //Initialise the section to be shown
        
        gui.getSectionPanel().removeAll();
        gui.getTestName().setText(test.getName());
        Section firstSection=test.getSections().get(ActualSection);
        gui.getSectionName().setText("<html>Section: <b>"+firstSection.getName() + "</b></html>");
        gui.getTime().setText(convertTimeFormat(firstSection.getTimeLimit()));
        initialiseSubsection(firstSection);
        
        //////
        //OBSERVATIONS: change names of variables and methods: initialiseTest() , firstSection , initialiseSubSection()
        ////////
    }

    //to convert minutes into String in clock format
    public static String convertTimeFormat(int minutes){            
            int millis = minutes * 60000;
            String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
            TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
            TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
            return hms;
    }
    
    void setTimer(Section section){
        if (isStudent){
            int timeMinutes = section.getTimeLimit();
            int timeMilli = timeMinutes*60000;
            timerLabel= new TimerLabel(timeMilli,this);
            gui.setTimer(timerLabel);
            timerLabel.run();
        }
    }
    
    void initialiseSubsection(Section section){
     
        //set the timer and make it run
        setTimer(section);
        
        //the following code paints all the test in a java swing container. Components are added dinamically and formated using HTML code
        //every question is shown in a especific way depending on its type (MCQ or FIBQ), and a Event listener to its components is added in order to obtain the 
        //students answer
        LinkedList <SubSection> subsections=section.getSubSections();
        for(int i=0; i<subsections.size(); i++){        //loop for every subsection in the actual section
            displaySubSection(ActualSection,i);
        }

        gui.getSectionPanel().revalidate();
        //show end of test here
        if (ActualSection == test.getSections().size() - 1) {
            changeBtnNextToFinish();
        }
    }
    
    private void insertImage(String imageFile ,JPanel panelAdd){
        
        if(!imageFile.equals("No image")){
            
            imageFile=imagePath+imageFile;
            
            JLabel label = new JLabel("Hover mouse here to view image");
            label.setForeground(new Color(0, 0, 255));
            label.setFont(new Font("Lucida Grande", 2, 13));
            label.setToolTipText("<html><img src=\"file:"+imageFile+"\"></html>");
            
            Border emptyBorder = BorderFactory.createEmptyBorder(5, 20, 0, 0);
            label.setBorder(emptyBorder);
            
            JPanel pnlTemp = new JPanel();
            pnlTemp.setName("ImagePanel");
            pnlTemp.setLayout(new FlowLayout(FlowLayout.LEFT));
            pnlTemp.setBackground(panelAdd.getBackground());
            pnlTemp.add(label);

            panelAdd.add(pnlTemp);

        }
    }
    
    public JLabel createSubSectionHeader(int subSection,String sectionName){
            JLabel subSectionHeader=new JLabel("<html>" + (subSection+1)+". <u>"+sectionName+ "</u></html>");
            subSectionHeader.setFont(new Font("Tahoma", Font.PLAIN, 22));
            subSectionHeader.setBorder(BorderFactory.createEmptyBorder(15, 10, 0, 0));
            return subSectionHeader;
    }
    
    public void displaySubSection(int section,int subSection){
    //set up area of subsection and print info
            
            SubSection subsection=test.getSections().get(section).getSubSections().get(subSection);
            gui.getSectionPanel().add(createSubSectionHeader(subSection,subsection.getName()));
            
            for (int questionNumber=0; questionNumber < subsection.getQuestions().size(); questionNumber++){ //loop for every question in the subsection
                Question tmpQuestion = subsection.getQuestions().get(questionNumber);
                
                String questionAddress=(subSection+1)+"."+(questionNumber+1);
                
                JLabel lblQuestionText = new JLabel(questionAddress +" "+ tmpQuestion.getQuestionTxt());
                lblQuestionText.setFont(new Font("Lucida", Font.BOLD, 14));
                
                    //display question if it is MCQ
                    if (tmpQuestion instanceof MCQ){                  
                        createMCQ(lblQuestionText, tmpQuestion, section, subSection, questionNumber);
                    } 
                    
                    //display question if it is FIBQ
                    if (tmpQuestion instanceof FIBQ || tmpQuestion instanceof MFIBQ){
                        createFIBQ(tmpQuestion, questionAddress, section, subSection, questionNumber);
                    }
            }
    }
    
    private void createMCQ(JLabel lblQuestionText, Question tmpQuestion, int section, int subSection, int questionNumber){
        lblQuestionText.setBorder(BorderFactory.createEmptyBorder(5, 25, 0, 0));
                        
        gui.getSectionPanel().add(lblQuestionText);

        insertImage(tmpQuestion.getImageFile(),gui.getSectionPanel());

        MCQ tempMCQ= (MCQ)tmpQuestion;
        ButtonGroup group = new ButtonGroup();

        for (int k=0; k<tempMCQ.getPossAns().size(); k++){
            final JRadioButton radioBtn = new JRadioButton(tempMCQ.getPossAns().get(k));

            radioBtn.setName(section+","+subSection+","+questionNumber+","+k);

            radioBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                setAnswer(test, radioBtn.getName());
            }
        });

            group.add(radioBtn);
            radioBtn.setBorder(BorderFactory.createEmptyBorder(0, 40, 0, 0));

            gui.getSectionPanel().add(radioBtn);
        }
    }
    
    private void createFIBQ(Question tmpQuestion, String questionAddress, int section, int subSection, int questionNumber){
        Question tempFIBQ;
            if (tmpQuestion instanceof FIBQ) { tempFIBQ = (FIBQ) tmpQuestion; } 
            else { tempFIBQ = (MFIBQ) tmpQuestion; }

        JPanel jplFIBQ = new JPanel();
            if (tmpQuestion instanceof FIBQ) { jplFIBQ.setName("FIBQ"); } 
            else { jplFIBQ.setName("MFIBQ"); }
        jplFIBQ.setLayout(new FlowLayout(FlowLayout.LEFT));
        jplFIBQ.setBackground(gui.getSectionPanel().getBackground());
        gui.getSectionPanel().add(jplFIBQ);

        JLabel lblQuestionNum = new JLabel(questionAddress + " ");
        lblQuestionNum.setBorder(BorderFactory.createEmptyBorder(5, 20, 0, 0));
        lblQuestionNum.setFont(new Font("Lucida", Font.BOLD, 14));
        jplFIBQ.add(lblQuestionNum);

        String question = tempFIBQ.getQuestionTxt();

        int oldPosition = 0;
        for (int index = question.indexOf("<blank>"); index >= 0;
                index = question.indexOf("<blank>", index + 1)) {
            JLabel lblPartOfQuestion = new JLabel(question.substring(oldPosition, index));
            lblPartOfQuestion.setFont(new Font("Lucida", Font.BOLD, 14));
            jplFIBQ.add(lblPartOfQuestion);

            final JTextField txtBlank = new JTextField("");
                if (tmpQuestion instanceof FIBQ) { txtBlank.setName(section + "," + subSection + "," + questionNumber + ","); } 
                else { txtBlank.setName(section + "," + subSection + "," + questionNumber + "," + index + ",MFIBQ"); }
            txtBlank.setPreferredSize(new Dimension(100, 23));
            txtBlank.getDocument().addDocumentListener(new DocumentListener(){

                        @Override
                        public void insertUpdate(DocumentEvent e) {
                            setAnswer(test, txtBlank.getName() + txtBlank.getText());
                        }
                        @Override
                        public void removeUpdate(DocumentEvent e) {
                            setAnswer(test, txtBlank.getName() + txtBlank.getText());
                        }
                        @Override
                        public void changedUpdate(DocumentEvent e) {
                             setAnswer(test, txtBlank.getName() + txtBlank.getText());
                        }
                    });
                    
            jplFIBQ.add(txtBlank);
            oldPosition = index + 7;
        }

        JLabel lblLastPartOfQuestion = new JLabel(question.substring(oldPosition));
        lblLastPartOfQuestion.setFont(new Font("Lucida", Font.BOLD, 14));
        jplFIBQ.add(lblLastPartOfQuestion);
        insertImage(tmpQuestion.getImageFile(), gui.getSectionPanel());
        jplFIBQ.revalidate();
    }
    
    public void timeExpired(){
        
        //if timerLabel class detects time has expired this method is called
        gui.getSectionPanel().setEnabled(false);
        messageDialogTimeExp("The Time for this section has Expired ! ", gui);
        gui.getBtnNextSection().doClick();
        gui.getSectionPanel().setEnabled(true);
    }

    void setAnswer(Test test,String name){
        
        //set answer of the actual question answered by student
        String location[] = name.split(",");
        int sectionLoc = Integer.parseInt(location[0]);
        int subSectionLoc = Integer.parseInt(location[1]);
        int questionLoc = Integer.parseInt(location[2]);
        
        String currentLocation=sectionLoc+","+subSectionLoc+","+questionLoc;
        
        Question tmpQuestion = test.getSections().get(sectionLoc).getSubSections().get(subSectionLoc).getQuestions().get(questionLoc);
        String tmpAnswer="";
        
        if (location.length > 4){
            tmpAnswer = "";
            
            for (Object allObjects:gui.getSectionPanel().getComponents()){
                if (allObjects instanceof JPanel){
                    if (((JPanel)allObjects).getName().equals("MFIBQ")){                        
                        for (Object allTextBoxes:((JPanel)allObjects).getComponents()){
                            if (allTextBoxes instanceof JTextField){
                                String tempLocation[] = ((JTextField)(allTextBoxes)).getName().split(",");
                                //
                                if (currentLocation.equals(tempLocation[0]+","+tempLocation[1]+","+tempLocation[2])){
                                    tmpAnswer += ((JTextField)(allTextBoxes)).getText() + ",";
                                }
                            }
                        }
                    }
                }
            }
        tmpQuestion.setStudentAnswer(tmpAnswer); 
        return;
        }
        
    tmpQuestion.setStudentAnswer(location[3]);
    }

    //    DIALOGS     //

    public void messageDialogInfo(String message, Component cmp){

         JOptionPane.showMessageDialog(cmp, message, "Information",JOptionPane.INFORMATION_MESSAGE);
    }
    
    public void messageDialogResults (String message, Component cmp){
        
        JLabel label = new JLabel(message);
        label.setFont(new java.awt.Font("Lucida Grande",0, 12));
        label.setHorizontalTextPosition(javax.swing.SwingConstants.TRAILING);
        
        JLabel label2 = new JLabel(new javax.swing.ImageIcon(getClass().getResource("/images/results128.png")));
        JOptionPane.showMessageDialog(cmp, new JLabel[]{label2,label}, "Results",JOptionPane.WARNING_MESSAGE, new javax.swing.ImageIcon(getClass().getResource("/images/dum.ico")) );
    }
    
    public void messageDialogTimeExp (String message, Component cmp){
        JOptionPane.showMessageDialog(cmp, message, "Time Expired",JOptionPane.WARNING_MESSAGE , new javax.swing.ImageIcon(getClass().getResource("/images/time64.png")));
    }

    public void messageDialogWarning (String message, Component cmp){
        JOptionPane.showMessageDialog(cmp, message, "Warning",JOptionPane.WARNING_MESSAGE , new javax.swing.ImageIcon(getClass().getResource("/images/stop64.png")));
    }
    
    public void messageDialogOK (String message, Component cmp){
        JOptionPane.showMessageDialog(cmp, message, "OK", JOptionPane.INFORMATION_MESSAGE, new javax.swing.ImageIcon(getClass().getResource("/images/done64.png")));
    }
    
    //return true if the user wants to continue
    public boolean messageOptionDialog(String messageQuestion,Component cmp){
        Object[] options = {"Yes, continue","No, cancel!"};
        
        int i =JOptionPane.showOptionDialog(cmp,messageQuestion,"Question",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,new javax.swing.ImageIcon(getClass().getResource("/images/question64.png")),options,options[0]);
        
        if (i==0){
            return true;
        }
        else{
            return false;
        }
    }

    public void endTest(){
        if (isStudent){
            MainGUI mainGUI = new MainGUI();
            mainGUI.setVisible(true);
        }
    }
}